#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author       : Abmer
# @Date         : 2019-08-06 10:08:40
# @Link         : https://gitee.com/edrik
# @Modified by  : Amber
# @Modified at  : 2019-08-21 16:05:17
# @Version      : v.0.1


import logging
import os
from ..config import Config
from .mbtypes import *
# dll config
__ALL__ = ['api']
log = logging.getLogger(__name__)

DLL_ROOT = Config.DLL_ROOT
DLL_NAME = Config.DLL_NAME
DLL_NAME_X64 = Config.DLL_NAME_X64
DLL_EXT = Config.DLL_EXT

log.info(' '.join((os.name, OS, ARCH)))


def find_wke_dll(name='node', ext='.dll'):
    # error message
    LOAD_ERROR = """SDK %s not found; ERROR: %s;
    Please verify that SDK is installed and its binaries (*.dll) are available in the path."""

    def _load(path):
        path = os.path.abspath(path)
        print('[Info] DLL of Miniblink path: %s' % path)
        # ctypes.cdll.LoadLibrary(path)
        return ctypes.CDLL(path)

    dllname = ARCH_32 and DLL_NAME or DLL_NAME_X64
    path = os.path.join(DLL_ROOT, dllname + DLL_EXT)
    try:
        wkedll = _load(path)
    except Exception as e:
        log.warn(LOAD_ERROR % (path, e))
        log.warn('[Error] Failed found dll at %s. Retrying...' % path)
        path = os.path.join('.', dllname + DLL_EXT)
        log.warn(path)
        try:
            wkedll = _load(path)
        except Exception as e:
            raise ValueError(LOAD_ERROR % (path, e))
    if not wkedll:
        raise ImportError(LOAD_ERROR % (path, 'missing .dll'))
    return wkedll


wkeVersion = FN(UINT)
wkeVersionString = FN(PUTF8)
wkeInitialize = FN(VOID)
wkeInit = FN(VOID)
wkeCreateWebWindow = FN(PVOID, INT, WEB_VIEW, INT, INT, INT, INT)
wkeGetWindowHandle = FN(HWND, WEB_VIEW)
wkeGetHostHWND = FN(HWND, WEB_VIEW)
wkeGlobalExec = FN(JS_EXEC_STATE, WEB_VIEW)
wkeWebFrameGetMainFrame = FN(HWND_FRAME, WEB_VIEW)
wkeMoveToCenter = FN(VOID, WEB_VIEW)

wkeSetWindowTitleW = FN(VOID, WEB_VIEW, PWCHAR)

# wkeNavigationCallback = FN(BOOL, INT, PVOID, INT, PVOID)
wkeOnNavigation = FN(VOID, WEB_VIEW, FN_CALLBACK, PVOID)
wkeSetDebugConfig = FN(VOID, WEB_VIEW, PCHAR, PCHAR)

wkeGetString = FN(PUTF8, PVOID)
wkeGetStringW = FN(PWCHAR, PVOID)
# JS
wkeRunJSW = FN(JS_VALUE, WEB_VIEW, PWCHAR)
wkeRunJS = FN(JS_VALUE, WEB_VIEW, PUTF8)
wkeRunJsByFrame = FN(JS_VALUE, WEB_VIEW, HWND_FRAME, PUTF8, BOOL)

wkeJsBindFunction = FN(VOID, PCHAR, FN_CALLBACK, PVOID, UINT)

jsArg = FN(JS_VALUE, JS_EXEC_STATE, INT)
jsArgCount = FN(INT, JS_EXEC_STATE)
jsArgType = FN(JS_VALUE, JS_EXEC_STATE, INT)

jsArrayBuffer = FN(JS_VALUE, JS_EXEC_STATE, PCHAR, SIZE_T)

jsBindGetter = FN(VOID, PCHAR, FN_JS_NATIVE)
jsBindSetter = FN(VOID, FN_JS_NATIVE)

jsCall = FN(PVOID, PVOID, PVOID, PVOID, PVOID, INT)
jsCallGlobal = FN(PVOID, PVOID, PVOID, PVOID, INT)
jsDouble = FN(JS_VALUE, DOUBLE)
jsEmptyObject = FN(JS_VALUE, JS_EXEC_STATE)

jsEvalExW = FN(JS_VALUE, JS_EXEC_STATE, PWCHAR, BOOL)
jsEvalW = FN(JS_VALUE, JS_EXEC_STATE, PWCHAR)

jsFunction = FN(JS_VALUE, JS_EXEC_STATE, P_JS_DATA_X)
jsGC = FN(VOID)
jsGet = FN(JS_VALUE, JS_EXEC_STATE, JS_VALUE, PCHAR)
jsGetArrayBuffer = FN(P_WKE_MEM_BUF, JS_EXEC_STATE, JS_VALUE)
jsGetAt = FN(JS_VALUE, JS_EXEC_STATE, JS_VALUE, INT)
jsGetData = FN(P_JS_DATA, JS_EXEC_STATE, JS_VALUE)
jsGetGlobal = FN(JS_VALUE, JS_EXEC_STATE, PCHAR)
jsGetKeys = FN(P_JS_KEYS, JS_EXEC_STATE, JS_VALUE)
jsGetLastErrorIfException = FN(P_J_EXCEPT_INFO, JS_EXEC_STATE)

jsGetLength = FN(INT, JS_EXEC_STATE, JS_VALUE)
jsGetWebView = FN(WEB_VIEW, JS_EXEC_STATE)

jsInt = FN(JS_VALUE, JS_EXEC_STATE, INT)
jsIsBoolean = FN(BOOL, JS_VALUE)
jsIsFalse = FN(BOOL, JS_VALUE)
jsIsNumber = FN(BOOL, JS_VALUE)
jsIsObject = FN(BOOL, JS_VALUE)
jsIsString = FN(BOOL, JS_VALUE)
jsIsTrue = FN(BOOL, JS_VALUE)
jsObject = FN(BOOL, JS_VALUE)

jsSet = FN(VOID, JS_EXEC_STATE, JS_VALUE, PCHAR, JS_VALUE)
jsSetAt = FN(VOID, JS_EXEC_STATE, JS_VALUE, PCHAR, JS_VALUE)
jsSetGlobal = FN(VOID, JS_EXEC_STATE, PCHAR, JS_VALUE)
jsSetLength = FN(VOID, JS_EXEC_STATE, JS_VALUE, INT)

jsString = FN(JS_VALUE, JS_EXEC_STATE, PUTF8)
jsStringW = FN(JS_VALUE, JS_EXEC_STATE, PWCHAR)
jsToDouble = FN(DOUBLE, JS_EXEC_STATE, JS_VALUE)
jsToInt = FN(INT, JS_EXEC_STATE, JS_VALUE)
jsToString = FN(PUTF8, JS_EXEC_STATE, JS_VALUE)
jsToStringW = FN(PWCHAR, JS_EXEC_STATE, JS_VALUE)
jsToTempString = FN(PUTF8, JS_EXEC_STATE, JS_VALUE)
jsToTempStringW = FN(PWCHAR, JS_EXEC_STATE, JS_VALUE)
jsTypeOf = FN(JS_TYPE, JS_VALUE)


class IWkeAPI(Structure):
    '''un usage'''

    _api = [
        'wkeCreateWebWindow',
        'wkeGetString',
        'wkeGetStringW',
        'wkeGetWindowHandle',
        'wkeGlobalExec',
        'wkeInit',
        'wkeInitialize',
        'wkeJsBindFunction',
        'wkeMoveToCenter',
        'wkeOnNavigation',
        'wkeRunJS',
        'wkeRunJsByFrame',
        'wkeRunJSW',
        'wkeSetDebugConfig',
        'wkeSetWindowTitleW',
        'wkeVersion',
        'wkeVersionString',
        'wkeWebFrameGetMainFrame',
        "jsArg",
        "jsArgCount",
        "jsArgType",
        "jsArrayBuffer",
        # "jsBindFunction",
        # "jsBindGetter",
        # "jsBindSetter",
        "jsCall",
        "jsCallGlobal",
        "jsDouble",
        "jsEmptyObject",
        "jsEvalExW",
        "jsEvalW",
        "jsFunction",
        "jsGC",
        "jsGet",
        "jsGetArrayBuffer",
        "jsGetAt",
        "jsGetData",
        "jsGetGlobal",
        "jsGetKeys",
        "jsGetLastErrorIfException",
        "jsGetLength",
        "jsGetWebView",
        # "jsInt",
        "jsIsBoolean",
        "jsIsFalse",
        "jsIsNumber",
        "jsIsObject",
        "jsIsString",
        "jsIsTrue",
        "jsObject",
        "jsSet",
        "jsSetAt",
        "jsSetGlobal",
        "jsSetLength",
        "jsString",
        "jsStringW",
        "jsToDouble",
        "jsToInt",
        "jsToString",
        "jsToStringW",
        "jsToTempString",
        "jsToTempStringW",
        "jsTypeOf",
    ]

    def _make_fields(names):
        ctx = globals()
        fields = [(name, ctx[name]) for name in names if name in ctx]
        return fields

    _fields_ = _make_fields(_api)


def _wke_api():
    wke = find_wke_dll()
    api = {}
    ctx = globals()
    for name in IWkeAPI._api:
        fn_type = ctx[name]
        # wke.__getattr__(name)
        fn = getattr(wke, name)
        fn.restype = fn_type._restype_
        fn.argtypes = fn_type._argtypes_
        api[name] = fn
    log.info('[Info] Wke API init finished.')
    return api, wke


class WkeAPI():
    """docstring for WkeAPI"""
    api, wke = _wke_api()

    def __init__(self, fn=None):
        self.fn = fn

    def __getattr__(self, api_name):
        fn = self.api.get(api_name)
        if not fn:
            log.info('[Warn]\t%s not init..' % api_name)
            fn = getattr(self.wke, api_name)
        return fn

    def __call__(self, *args, **kwargs):
        raise ValueError('Forbid call!')


@WkeAPI
def wke_api():
    pass


api = wke_api
